/*
 *  Copyright (c) 2008 Howard Shank
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to use, 
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 
 * Software, and to permit persons to whom the Software is furnished to do so, subject 
 * to the following conditions:

 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Specialized;
using System.Collections;

namespace AjaxCtrlsExtenders
{
    /// <summary>
    /// AjaxDragDropListBox control is a standard listbox control enhanced to provide client side
    /// updating of option list and selections. With the addition of hidden fields, the control is
    /// able to retrieve client side updates and on post back, it updates the contents of the control
    /// from the view state and the addition and removal values in the hidden fields.
    /// </summary>
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:AjaxDragDropListBox runat=server></{0}:AjaxDragDropListBox>")]
//    [Designer(typeof(AjaxDragDropListBoxDesigner))]
    public class AjaxDragDropListBox : ListBox, IPostBackDataHandler
    {
        /// <summary>
        /// Name of the hidden add field for this instance.
        /// </summary>
        protected string dataListControlNameAdd
        {
            get { return this.ClientID + "DataAdd"; }
        }

        /// <summary>
        /// Name of the hidden remove field for this instance
        /// </summary>
        protected string dataListControlNameRemove
        {
            get { return this.ClientID + "DataRemove"; }
        }

        /// <summary>
        /// Override OnPreRender in order to register the postback and hidden fields.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (Page != null)
                Page.RegisterRequiresPostBack(this);
            Page.ClientScript.RegisterHiddenField(this.dataListControlNameAdd, "");
            Page.ClientScript.RegisterHiddenField(this.dataListControlNameRemove, "");
        }

        /// <summary>
        /// Override the Render
        /// </summary>
        /// <param name="output"></param>
        protected override void RenderContents(HtmlTextWriter output)
        {
            base.RenderContents(output);
            output.Write(Text);
        }

        /// <summary>
        /// Handle the data returned on a postback.
        /// Process the viewstate, then add and remove values from the hidden field values.
        /// Adjusts for selected indexes, etc.
        /// </summary>
        /// <param name="postDataKey"></param>
        /// <param name="postCollection"></param>
        /// <returns></returns>
        bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
        {
            string[] postedItems = postCollection.GetValues(postDataKey);
            bool result = false;

            if (postedItems == null)
            {
                if (this.SelectedIndex != -1)
                {
                    result = true;
                }
            }
            else
            {
                // If selection is in single mode
                if (this.SelectionMode == ListSelectionMode.Single)
                {
                    if (postedItems != null)
                    {
                        // Process first item, since we have a single selection
                        // ListItem item = this.FindByValueInternal(postedItems[0]);
                        int index = this.FindByValueInternal(postedItems[0]);
                        //Items.IndexOf(item);
                        if (this.SelectedIndex != index)
                        {
                            // Change occurred
                            this.SelectedIndex = index;
                            result = true;
                        }
                    }
                }

                // Else, the selection mode is multiple
                    int numberOfItemsSelected = postedItems.Length;

                    // Old selected items
                    ArrayList oldSelectedItems = this.SelectedIndicesInternal;

                    // An empty arraylist of the currently selected items on the clisnde side
                    ArrayList currentlySelectedItems = new ArrayList(numberOfItemsSelected);

                    // Fill in all the indicies of the posted selected items
                    for (int i = 0; i < numberOfItemsSelected; i++)
                    {
                        currentlySelectedItems.Add(this.FindByValueInternal(postedItems[i]));
                    }

                    // Get the number of currently selected indicies
                    int numberOfSelectedItems = 0;
                    if (oldSelectedItems != null)
                    {
                        numberOfSelectedItems = oldSelectedItems.Count;
                    }

                    // Check if both numbers are equal
                    if (numberOfItemsSelected == numberOfSelectedItems)
                    {
                        for (int j = 0; j < numberOfItemsSelected; j++)
                        {
                            int oldSelect = Convert.ToInt32(currentlySelectedItems[j]);
                            int currentSelect = Convert.ToInt32(oldSelectedItems[j]);

                            if (oldSelect != currentSelect)
                            {
                                // Explicitly mark the item selected
                                this.Items[j].Selected = true;
                                result = true;
                            }
                        }
                    }
                    else
                    {
                        // The number of selected items has changed
                        result = true;
                    }

                // Reset all selections to the newly selected items
                if (result)
                {
                    this.SelectInternal(currentlySelectedItems);
                }
            }
            // Remove the items from the Items collection
            // that were marke for deletion on client side// Handle items added on Client Side
            string itemsRemoved = postCollection[this.dataListControlNameRemove];
            string[] itemsRemovedCol = itemsRemoved.Split(',');
            if (itemsRemovedCol != null)
            {
                if ((itemsRemovedCol.Length > 0) && (itemsRemovedCol[0] != ""))
                {
                    for (int i = 0; i < itemsRemovedCol.Length; i++)
                    {
                        ListItem itemToRemove = this.Items.FindByValue(itemsRemovedCol[i]);

                        // Remove from the Items Collection
                        Items.Remove(itemToRemove);
                    }
                    result = true;
                }
            }

            // Handle items added on Client Side
            string itemsAdded = postCollection[this.dataListControlNameAdd];
            string[] itemsCol = itemsAdded.Split(',');
            if (itemsCol != null)
            {
                if ((itemsCol.Length > 0) && (itemsCol[0] != ""))
                {
                    // counter to validate returnValue
                    int counter = -1;
                    for (int i = 0; i < itemsCol.Length; i++)
                    {
                        string buf = itemsCol[i];
                        string[] itemsTokens = buf.Split('|');

                        // Check if already added to the collection
                        //ListItem it = this.Items.FindByValue(itemsTokens[1]);
                        //if (it == null)
                        if(this.Items.FindByValue(itemsTokens[1]) == null)
                        {
                            //string text = itemsTokens[0];
                            //string id = itemsTokens[1];
                            //ListItem item = new ListItem(new string(itemsTokens[0], new string(itemsTokens[1]);
                            Items.Add(new ListItem(itemsTokens[0].ToString(), itemsTokens[1].ToString()));

                            // Updata counter
                            counter++;
                        }
                    }
                    result = counter > -1 ? true : false;
                }
            }

            return result;

        }
        /// <summary>
        /// Raise the event if index selection has changed.
        /// </summary>
        void IPostBackDataHandler.RaisePostDataChangedEvent()
        {
            OnSelectedIndexChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Gets an ArrayList of all selected indicies in the ListBox
        /// </summary>
        internal virtual ArrayList SelectedIndicesInternal
        {
            get
            {
                ArrayList selectedItems = new ArrayList();
                for (int index = 0; index < this.Items.Count; index++)
                {
                    if (this.Items[index].Selected)
                    {
                        // Add the index of the selected item
                        selectedItems.Add(index);
                    }
                }
                return selectedItems;
            }
        }

        internal int FindByValueInternal(string value)
        {
            int index = 0;
            foreach (ListItem currentItem in this.Items)
            {
                if (currentItem.Value.Equals(value))
                {
                    return index;
                }
                index++;
            }
            return -1;
        }

        /// <summary>
        /// Selects required items
        /// </summary>
        /// <param name="selectedIndices"></param>
        internal void SelectInternal(ArrayList selectedIndices)
        {
            this.ClearSelection();
            //foreach (int selectedIdx in selectedIndices)
            //{
            //    if (selectedIdx >= 0 && selectedIdx < this.Items.Count)
            //    {
            //        this.Items[selectedIdx].Selected = true;
            //    }
            //}
            for (int num1 = 0; num1 < selectedIndices.Count; num1++)
            {
                int num2 = (int)selectedIndices[num1];
                if ((num2 >= 0) && (num2 < this.Items.Count))
                {
                    this.Items[num2].Selected = true;
                }
            }
        }
    }
}
